/**
 * vue3 响应式原理
 * */

/**
 * 前置知识：
 * 1 Proxy 对象用于创建一个对象的代理，从而实现基本操作的拦截和自定义（如属性查找、赋值、枚举、函数调用等） https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
 * 2 Reflect 是一个内置的对象，它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers (en-US)的方法相同。Reflect不是一个函数对象，因此它是不可构造的。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
 * 3 WeakMap 对象是一组键/值对的集合，其中的键是弱引用的。其键必须是对象，而值可以是任意的。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
 */

/**
 * 判断是否是对象
 * @param target
 * @return {boolean}
 */
function isObject(target) {
    return typeof target == 'object' || target === null
}

/**
 * 判断对象是否包含指定属性
 * @param target
 * @param key
 * @return {boolean}
 */
function hasOwn(target, key) {
    return target.hasOwnProperty(key)
}

/**
 * 记录已经代理的对象 放置：原对象和代理过的对象
 */
const toProxy = new WeakMap()
/**
 * 记录被代理的原对象 放置：原对象
 */
const toRaw = new WeakMap()

/**
 * 响应式
 * @param target
 */
function reactive(target) {
    return creatReactiveObject(target)
}

/**
 * 创建响应式对象
 * @param target
 */
function creatReactiveObject(target) {
    if (!isObject(target)) {
        return target
    }
    const proxy = toProxy.get(target)
    if(proxy){
        // reactive(obj) 如果已经代理过了 就将代理过的结果返回即可
        return proxy
    }
    if(toRaw.has(target)){
        // reactive(proxy) target是已经代理过的对象 就将当前target对象返回即可
        // 放置重复代理
        return target
    }
    // receiver 是代理后的对象
    const handler = {
        get(target, key, receiver) {
            console.log('get 获取', key)
            const res = Reflect.get(target, key, receiver)
            // 如果取到的值是对象 就再次代理
            return isObject(res)?reactive(res):res;
        },
        set(target, key, value, receiver){
            console.log(key, value)
            if(!hasOwn(target, key)){
                // 不存在属性 新增
                console.log('新增属性')
            } else if(target[key] !== value){
                // 修改属性
                console.log('修改属性')
            }
            return Reflect.set(target, key,value, receiver);
        },
        deleteProperty(target, key) {
            console.log('delete 删除')
            return Reflect.deleteProperty(target, key);
        }
    }
    // 创建代理
    const observe = new Proxy(target, handler)
    // 记录 代理后的对象
    toProxy.set(target,observe)
    // // 记录 被代理的原对象
    toRaw.set(observe, target)
    return observe
}

const obj = {
    name: '曾小晖',
    interest: {
        photography: '摄影',
        running: '跑步',
        badminton: '羽毛球',
    },
    love: [1, 2, 3]
}

const proxy = reactive(obj)
// proxy.name = '真帅'
// console.log(proxy.name)
// delete proxy.name
// proxy.love.length = 0
// 处理不存在的属性
// proxy.interest.b = 2
// console.log(proxy)
// console.log(proxy.interest.b)
// let b = reactive(obj)
//  reactive(obj)
// reactive(obj)
// const a = reactive(proxy)
// b.name = '真帅'
// proxy.name = '真帅'
// console.log(proxy.name)
// console.log(proxy)
// console.log(b)
// console.log(b)
// proxy.love.push(4)
// proxy.love.length = 100
proxy.love.splice(2,1)
// console.log(proxy)
